// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <map>
#include <memory>
#include <string>
#include <vector>

#include <cryptopp/filters.h>
#include <cryptopp/hmac.h>
#include <cryptopp/sha.h>

#include "coin2/base/base64.h"
#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/nonce_manager.h"
#include "coin2/exchange/base/feed/processor.h"
#include "cryptopp/base64.h"

namespace coin2::exchange::gopax::feed_v1 {
namespace impl {

std::string CalcHmacSHA512(std::string_view decodedSecretKey, std::string_view request) {
  using CryptoPP::Base64Encoder;
  using CryptoPP::HashFilter;
  using CryptoPP::SHA512;
  using CryptoPP::StringSink;
  using CryptoPP::StringSource;

  CryptoPP::HMAC<SHA512> hmac(
      reinterpret_cast<CryptoPP::byte const*>(decodedSecretKey.data()),
      decodedSecretKey.size());
  std::string calculated_hmac;
  auto sink = std::make_unique<StringSink>(calculated_hmac);
  auto filter = std::make_unique<HashFilter>(hmac, sink.get());
  sink.release();

  StringSource(
      reinterpret_cast<CryptoPP::byte const*>(request.data()),
      request.size(),
      true,
      filter.get());
  filter.release();

  std::string encoded;
  StringSource(calculated_hmac, true, new Base64Encoder(new StringSink(encoded)));
  return encoded;
}

class GopaxRequestGenerator : public base::feed::RequestGenerator {
 public:
  std::string RegeneratePath(const std::string& orig_path, const std::string& key_filepath)
      override {
    CHECK_THROW(std::experimental::filesystem::exists(key_filepath))
        << "Wrong key file path of Gopax:" << key_filepath << "\n";
    std::ifstream ifs(key_filepath);
    nlohmann::json jf = nlohmann::json::parse(ifs);
    CHECK_THROW(jf.find("access_key") != jf.end()) << "Wrong format of key file of Gopax";
    CHECK_THROW(jf.find("secret_key") != jf.end()) << "Wrong format of key file of Gopax";

    std::string timestamp = std::to_string(GetCurrentTimestamp() / 1000000);
    auto request = fmt::format("t{}", timestamp);
    auto raw_signature = CalcHmacSHA512(::impl::base64_decode(jf["secret_key"]), request);
    auto path = fmt::format(
        "{}?apiKey={}&timestamp={}&signature={}",
        orig_path,
        ApiSignatureUtil::EscapeURL(jf["access_key"]),
        timestamp,
        ApiSignatureUtil::EscapeURL(raw_signature));
    return path;
  }
};

}  // namespace impl
using impl::GopaxRequestGenerator;
}  // namespace coin2::exchange::gopax::feed_v1
